package accx

import (
	"accx/appFramework/m_config"
	"accx/utils/httpClient"
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/cihub/seelog"
	"net"
	"net/http"
	"strconv"
	"time"
)

var NowAllHeartbeat *map[string]Heartbeat
var OldAllHeartbeat *map[string]Heartbeat

func InitNowAllHeartbeat() *map[string]Heartbeat {
	nowAllHeartbeat := make(map[string]Heartbeat)
	NowAllHeartbeat = &nowAllHeartbeat
	return NowAllHeartbeat
}
func GetNowAllHeartbeat() *map[string]Heartbeat {
	return NowAllHeartbeat
}
func InitOldAllHeartbeat() *map[string]Heartbeat {
	oldAllHeartbeat := make(map[string]Heartbeat)
	OldAllHeartbeat = &oldAllHeartbeat
	return OldAllHeartbeat
}
func GetOldAllHeartbeat() *map[string]Heartbeat {
	return OldAllHeartbeat
}

type Acclist struct {
	MaxNum   int        `json:"maxNum"`
	CurrNum  int        `json:"currNum"`
	NodeInfo []NodeInfo `json:"nodeInfo"`
}
type NodeInfo struct {
	Object    string `json:"object"`
	Group     string `json:"group"`
	Node      string `json:"node"`
	IP        string `json:"ip"`
	DeviceSep string `json:"deviceSep"`
	Status    string `json:"status"`
}
type TemporaryService struct {
	Group string `json:"ip"`
	Node  string `json:"port"`
}

type HandShake struct {
	Group             string             `json:"group"`
	Node              string             `json:"node"`
	IP                string             `json:"ip"`
	DeviceSep         string             `json:"deviceSep"`
	Priority          int                `json:"priority"`
	NfInstanceIds     NfInstanceIds      `json:"nfInstanceIds"`
	StaticIpSegment   []string           `json:"staticIpSegment"`
	TemporaryServices []TemporaryService `json:"temporaryServices"`
	Status            string             `json:"status"`
}

type NfInstanceIds struct {
	Amf string `json:"amf "`
	Smf string `json:"smf"`
}
type ResponseSucMsg struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Data    interface{} `json:"data"`
}
type Heartbeat struct {
	Group             string             `json:"group"`
	Node              string             `json:"node"`
	IP                string             `json:"ip"`
	DeviceSep         string             `json:"deviceSep"`
	Priority          int                `json:"priority"`
	StaticIpSegment   []string           `json:"staticIpSegment"`
	TemporaryServices []TemporaryService `json:"temporaryServices"`
}

func (handShake *HandShake) GoHandShake() {
	myaccx := m_config.MyAccxData
	var handshake HandShake
	handshake.Group = myaccx.Group
	handshake.Node = myaccx.Node
	handshake.IP = myaccx.ServiceIp
	handshake.DeviceSep = myaccx.Deviceseq
	handshake.Priority = myaccx.Priority
	handshake.NfInstanceIds.Smf = myaccx.NfInstanceIds.Smf
	handshake.NfInstanceIds.Amf = myaccx.NfInstanceIds.Amf
	ticker := time.NewTicker(10 * time.Second)
	for range ticker.C {
		myaccx1 := m_config.MyAccxData
		allaccxdata := m_config.AccxAndAllaccxData
		for _, v := range myaccx1.StaticIpSegment {
			handshake.StaticIpSegment = append(handshake.StaticIpSegment, v)
		}
		for _, v := range myaccx1.TemporaryService {
			var t TemporaryService
			t.Node = v.Group
			t.Group = v.Node
			handshake.TemporaryServices = append(handshake.TemporaryServices, t)
		}
		for _, v := range allaccxdata.Accx {
			if v.Status == m_config.NoConnect || v.Status == m_config.LostConnection {
				handshake.Status = v.Status
				cli := httpClient.NewHttp2Client()
				marshalData, _ := json.Marshal(handshake)
				req, err := http.NewRequest("PATCH", "http://"+v.ServiceIp+":2333/v1/handShake", bytes.NewReader(marshalData))
				if err != nil {
					seelog.Error("%s handshake request is err:", v.ServiceIp, err)
					return
				}
				resp, err := cli.DoRequest(req)
				if err != nil {
					seelog.Error("%s handshake DoRequest is err:", v.ServiceIp, err)
					return
				}
				defer resp.Body.Close()
				if resp.StatusCode != http.StatusOK {
					seelog.Error("%s handshake resp is err", v.ServiceIp)
				}
			}
		}
	}
}
func (handShake *HandShake) Authentication() bool {
	accxAndAllaccxData := m_config.AccxAndAllaccxData
	for i, _ := range accxAndAllaccxData.Accx {
		if accxAndAllaccxData.Accx[i].Group == handShake.Group && accxAndAllaccxData.Accx[i].Node == handShake.Node &&
			accxAndAllaccxData.Accx[i].Deviceseq == handShake.DeviceSep && accxAndAllaccxData.Accx[i].ServiceIp == handShake.IP {
			if accxAndAllaccxData.Accx[i].Status == m_config.Fault {
				seelog.Error("handShake authentication is error,this is error one")
				return false
			}
			if accxAndAllaccxData.Accx[i].Status == m_config.LostConnection {
				accxAndAllaccxData.Accx[i].Status = m_config.ReconnectObservations
				accxAndAllaccxData.Accx[i].Priority = handShake.Priority
				var temps m_config.TemporaryService
				for _, v := range handShake.TemporaryServices {
					temps.Group = v.Group
					temps.Node = v.Node
					accxAndAllaccxData.Accx[i].TemporaryService = append(accxAndAllaccxData.Accx[i].TemporaryService, temps)
				}
				for _, v := range handShake.StaticIpSegment {
					accxAndAllaccxData.Accx[i].StaticIpSegment = append(accxAndAllaccxData.Accx[i].StaticIpSegment, v)
				}
				accxAndAllaccxData.Accx[i].NfInstanceIds.Smf = handShake.NfInstanceIds.Smf
				accxAndAllaccxData.Accx[i].NfInstanceIds.Amf = handShake.NfInstanceIds.Amf
				time.Sleep(5 * time.Minute)
				if accxAndAllaccxData.Accx[i].ReHeartNum > 50 {
					accxAndAllaccxData.Accx[i].Status = m_config.Connect
					//自己为继承者则更新自己的静态ip，继承，smf
					//2s后下线用户
					return true
				}
				accxAndAllaccxData.Accx[i].Status = m_config.Fault
				return false
			}
			if handShake.Status == m_config.LostConnection {
				time.Sleep(5 * time.Minute)
			}
			accxAndAllaccxData.Accx[i].Priority = handShake.Priority
			var temps m_config.TemporaryService
			for _, v := range handShake.TemporaryServices {
				temps.Group = v.Group
				temps.Node = v.Node
				accxAndAllaccxData.Accx[i].TemporaryService = append(accxAndAllaccxData.Accx[i].TemporaryService, temps)
			}
			for _, v := range handShake.StaticIpSegment {
				accxAndAllaccxData.Accx[i].StaticIpSegment = append(accxAndAllaccxData.Accx[i].StaticIpSegment, v)
			}
			accxAndAllaccxData.Accx[i].NfInstanceIds.Smf = handShake.NfInstanceIds.Smf
			accxAndAllaccxData.Accx[i].NfInstanceIds.Amf = handShake.NfInstanceIds.Amf
			accxAndAllaccxData.Accx[i].Status = m_config.Connect
			return true
		}
	}
	return false
}
func Goheart() {
	allaccxdata := m_config.AccxAndAllaccxData
	myaccx := m_config.MyAccxData
	var heartbeat Heartbeat
	heartbeat.Group = myaccx.Group
	heartbeat.Node = myaccx.Node
	heartbeat.IP = myaccx.ServiceIp
	heartbeat.DeviceSep = myaccx.Deviceseq
	heartbeat.Priority = myaccx.Priority
	ticker := time.NewTicker(10 * time.Second)
	for range ticker.C {
		heartbeat.StaticIpSegment = myaccx.StaticIpSegment
		if len(myaccx.TemporaryService) != 0 {
			for _, v := range myaccx.TemporaryService {
				var t TemporaryService
				t.Node = v.Group
				t.Group = v.Node
				heartbeat.TemporaryServices = append(heartbeat.TemporaryServices, t)
			}
		}
		for i, v := range allaccxdata.Accx {
			if v.Status == m_config.Connect || v.Status == m_config.ReconnectObservations {
				if v.HeartbeatErrNum.Num > 2 {
					allaccxdata.Accx[i].Status = m_config.LostConnection
					allaccxdata.Accx[i].HeartbeatErrNum.Num = 0
					allaccxdata.Accx[i].ReHeartNum = 0
					allaccxdata.Accx[i].HeartbeatErrNum.Time = time.Unix(1609459200, 0)
					PeerNfDeRegist(v.NfInstanceIds.Smf)
					PeerNfDeRegist(v.NfInstanceIds.Amf)
					GoTemplate(v)
					return
				}
				cli := httpClient.NewHttp2Client()
				marshalData, _ := json.Marshal(heartbeat)
				req, err := http.NewRequest("PATCH", "http://"+v.ServiceIp+":2333/v1/heartbeat", bytes.NewReader(marshalData))
				if err != nil {
					seelog.Error("%s heartbeat request is err:", v.ServiceIp, err)
					return
				}
				resp, err := cli.DoRequest(req)
				if err != nil {
					seelog.Error("%s heartbeat DoRequest is err:", v.ServiceIp, err)
					if allaccxdata.Accx[i].HeartbeatErrNum.Num < 1 {
						allaccxdata.Accx[i].HeartbeatErrNum.Num = 1
					} else {
						if time.Now().Sub(allaccxdata.Accx[i].HeartbeatErrNum.Time).Abs() > 25*time.Second {
							allaccxdata.Accx[i].HeartbeatErrNum.Num = 1
						} else {
							allaccxdata.Accx[i].HeartbeatErrNum.Num += 1
						}
					}
					allaccxdata.Accx[i].HeartbeatErrNum.Time = time.Now()
					return
				}
				defer resp.Body.Close()
				if resp.StatusCode != http.StatusOK {
					seelog.Error("%s heartbeat resp is err", v.ServiceIp)
					return
				}
				if v.Status == m_config.ReconnectObservations {
					allaccxdata.Accx[i].ReHeartNum += 1
				}
			}
		}
	}
}
func (heartbeat *Heartbeat) Authentication() bool {
	accxAndAllaccxData := m_config.AccxAndAllaccxData
	for i, _ := range accxAndAllaccxData.Accx {
		if accxAndAllaccxData.Accx[i].Group == heartbeat.Group && accxAndAllaccxData.Accx[i].Node == heartbeat.Node &&
			accxAndAllaccxData.Accx[i].Deviceseq == heartbeat.DeviceSep && accxAndAllaccxData.Accx[i].ServiceIp == heartbeat.IP {
			if accxAndAllaccxData.Accx[i].Status == m_config.LostConnection || accxAndAllaccxData.Accx[i].Status == m_config.NoConnect ||
				accxAndAllaccxData.Accx[i].Status == m_config.Fault {
				seelog.Error("heartbeat authentication is error,Please Handshake")
				return false
			}
			if accxAndAllaccxData.Accx[i].Status == m_config.ReconnectObservations {
				accxAndAllaccxData.Accx[i].ReHeartNum += 1
			}
			return true
		}
	}
	seelog.Error("heartbeat authentication is error,Identification unknown")
	return false
}

func (heartbeat *Heartbeat) SaveHeartData() bool {
	value, ok := (*OldAllHeartbeat)[heartbeat.Group+heartbeat.Node]
	var lostip, newip []string
	if ok {
		lostip, newip = findDiffElements(value.StaticIpSegment, heartbeat.StaticIpSegment)
	} else {
		accxAndAllaccxData := m_config.AccxAndAllaccxData
		for _, v := range accxAndAllaccxData.Accx {
			if v.Group == heartbeat.Group && v.Node == heartbeat.Node {
				lostip, newip = findDiffElements(v.StaticIpSegment, heartbeat.StaticIpSegment)
			}
		}
	}
	if len(newip) != 0 {
		removeip := removeElements(m_config.MyAccxData.StaticIpSegment, heartbeat.StaticIpSegment)
		if len(removeip) != len(m_config.MyAccxData.StaticIpSegment) { //如果新增的ip里有本端的继承ip（继承重选）
			m_config.MyAccxData.StaticIpSegment = removeip
			m_config.MyAccxData.TemporaryService = updatatemporaryservice(m_config.MyAccxData.TemporaryService, heartbeat.TemporaryServices)
			LocalSmfUpdate()
		}
	}
	if len(newip) != 0 || len(lostip) != 0 {
		accxAndAllaccxData := m_config.AccxAndAllaccxData
		var nfinstanceid string
		for i, _ := range accxAndAllaccxData.Accx {
			if accxAndAllaccxData.Accx[i].Group == heartbeat.Group && accxAndAllaccxData.Accx[i].Node == heartbeat.Node &&
				accxAndAllaccxData.Accx[i].Deviceseq == heartbeat.DeviceSep && accxAndAllaccxData.Accx[i].ServiceIp == heartbeat.IP {
				nfinstanceid = accxAndAllaccxData.Accx[i].NfInstanceIds.Smf
				var temporaryservices []m_config.TemporaryService
				var temporaryservice m_config.TemporaryService
				for _, v := range heartbeat.TemporaryServices {
					temporaryservice.Group = v.Group
					temporaryservice.Node = v.Node
					temporaryservices = append(temporaryservices, temporaryservice)
				}
				accxAndAllaccxData.Accx[i].TemporaryService = temporaryservices
				accxAndAllaccxData.Accx[i].StaticIpSegment = heartbeat.StaticIpSegment
			}
		}

		PeerSmfUpdate(heartbeat.StaticIpSegment, nfinstanceid)
		//TODO:如果我是重连或者对端是重连，等状态改变再去注册
		changeip := append(lostip, newip...)
		//ticker := time.Tick(time.Second)
		//for {
		//	select {
		//	case <-ticker:
		//		if accxAndAllaccxData.Accx[i].Status == m_config.Connect {
		//			Deregist(changeip)
		//			return true
		//		}
		//	}
		//}
		Deregist(changeip)
	}
	(*OldAllHeartbeat)[heartbeat.Group+heartbeat.Node] = *heartbeat
	return true
}
func findDiffElements(sliceA, sliceB []string) ([]string, []string) {
	diffInA := make([]string, 0)
	diffInB := make([]string, 0)
	for _, elementA := range sliceA {
		found := false
		for _, elementB := range sliceB {
			if elementA == elementB {
				found = true
				break
			}
		}
		if !found {
			diffInA = append(diffInA, elementA)
		}
	}
	for _, elementB := range sliceB {
		found := false
		for _, elementA := range sliceA {
			if elementB == elementA {
				found = true
				break
			}
		}
		if !found {
			diffInB = append(diffInB, elementB)
		}
	}
	return diffInA, diffInB
}
func updatatemporaryservice(sliceA []m_config.TemporaryService, sliceB []TemporaryService) []m_config.TemporaryService {
	result := make([]m_config.TemporaryService, 0)
	for _, elementA := range sliceA {
		found := false
		for _, elementB := range sliceB {
			if elementA.Node == elementB.Node && elementA.Group == elementB.Group {
				found = true
				break
			}
		}
		if !found {
			result = append(result, elementA)
		}
	}
	return result
}
func removeElements(sliceA, sliceB []string) []string {
	result := make([]string, 0)
	for _, elementA := range sliceA {
		found := false
		for _, elementB := range sliceB {
			if elementA == elementB {
				found = true
				break
			}
		}
		if !found {
			result = append(result, elementA)
		}
	}
	return result
}
func GoTemplate(betTemplateAccx m_config.Accx) {
	priority := 10000
	serviceip := "192.168.255.255"
	for _, v := range m_config.AccxAndAllaccxData.Accx {
		if v.Status == m_config.Connect || v.Status == m_config.Me {
			if v.Priority == 0 {
				break
			}
			if priority > v.Priority {
				priority = v.Priority
				serviceip = v.ServiceIp
			} else if priority == v.Priority {
				if ipToBinary(serviceip) > ipToBinary(v.ServiceIp) {
					serviceip = v.ServiceIp
				}
			} else {
			}
		}
	}
	myaccxdata := m_config.MyAccxData
	if myaccxdata.ServiceIp == serviceip {
		var betempaccxtemp m_config.TemporaryService
		betempaccxtemp.Group = betTemplateAccx.Group
		betempaccxtemp.Node = betTemplateAccx.Node
		myaccxdata.TemporaryService = append(myaccxdata.TemporaryService, betempaccxtemp)
		myaccxdata.TemporaryService = append(myaccxdata.TemporaryService, betTemplateAccx.TemporaryService...)
		myaccxdata.StaticIpSegment = append(myaccxdata.StaticIpSegment, betTemplateAccx.StaticIpSegment...)
		LocalSmfUpdate()
		Deregist(betTemplateAccx.StaticIpSegment)
	}
}

func ipToBinary(ipStr string) string {
	ip := net.ParseIP(ipStr)
	if ip == nil {
		return ""
	}
	ip = ip.To4()
	var binary string
	for i := 0; i < len(ip); i++ {
		part := int(ip[i])
		binaryPart := strconv.FormatInt(int64(part), 2)
		binary += fmt.Sprintf("%08s", binaryPart)
	}
	return binary
}

func GetAcclist() Acclist {
	accxAndAllaccxData := m_config.AccxAndAllaccxData
	var acclist Acclist
	var nodeinfo NodeInfo
	online := 0
	all := 0
	for _, v := range accxAndAllaccxData.Accx {
		nodeinfo.Node = v.Node
		nodeinfo.Group = v.Group
		nodeinfo.IP = v.ServiceIp
		nodeinfo.DeviceSep = v.Deviceseq
		if v.Status == m_config.Connect || v.Status == m_config.Me {
			nodeinfo.Status = m_config.Start
			online++
		} else {
			nodeinfo.Status = m_config.Stop
		}
		if v.Status == m_config.Me {
			nodeinfo.Object = "local"
		} else {
			nodeinfo.Object = "peer"
		}
		all++
		acclist.NodeInfo = append(acclist.NodeInfo, nodeinfo)
	}
	acclist.MaxNum = all
	acclist.CurrNum = online
	seelog.Error("heartbeat authentication is error,Identification unknown")
	return acclist
}
